package org.swim.web.servlet;

import java.io.IOException;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONObject;

import org.swim.remote.entities.INotification;
import org.swim.remote.entities.IUser;
import org.swim.remote.exceptions.SwimException;
import org.swim.remote.exceptions.SwimException.Severity;
import org.swim.remote.exceptions.TokenException;
import org.swim.remote.exceptions.UserException;
import org.swim.remote.manager.AbilityManagerRemote;
import org.swim.remote.manager.CollaborationManagerRemote;
import org.swim.remote.manager.FriendshipManagerRemote;
import org.swim.remote.manager.UserManagerRemote;
import org.swim.utils.SwimLog;
import org.swim.web.servlet.consts.Action;
import org.swim.web.servlet.consts.ErrorHandling;
import org.swim.web.utils.SwimConst;
import org.swim.web.utils.SwimUtils;

public abstract class SwimServlet extends HttpServlet {

	public static final boolean EJB_CONNECTED = false;
	public static final boolean USERMANAGER_MOCKUP = false;
	public static final boolean ABILITYMANAGER_MOCKUP = false;
	public static final boolean FAKE_USER_LOGGED = true;
	public static final boolean GET_FAKE_TOKEN = false;

	private static final long serialVersionUID = 1L;

	private static UserManagerRemote umr;
	private static AbilityManagerRemote amr;
	private static CollaborationManagerRemote cmr;
	private static FriendshipManagerRemote fmr;

	public static final String ACTION = "a";

	public enum Method {
		GET, POST
	}

	public static class JspPages {
		public static final String PROFILE = "profile.jsp";
		public static final String MY_PROFILE = "my-profile.jsp";
		public static final String SEARCH = "search.jsp";
	}

	public static class SessionConst {
		public static final String TOKEN = "token";
	}

	protected static UserManagerRemote getUmr() {
		if (umr == null) {
			Context ic;
			try {
				ic = new InitialContext();
				umr = (UserManagerRemote) ic.lookup("swim/UserManager/remote");
			} catch (NamingException e) {
				e.printStackTrace();
			}
		}

		return umr;
	}

	protected static FriendshipManagerRemote getFmr() {
		if (fmr == null) {
			Context ic;
			try {
				ic = new InitialContext();
				fmr = (FriendshipManagerRemote) ic
						.lookup("swim/FriendshipManager/remote");
			} catch (NamingException e) {
				e.printStackTrace();
			}
		}

		return fmr;
	}

	protected static AbilityManagerRemote getAmr() {
		if (amr == null) {
			Context ic;
			try {
				ic = new InitialContext();
				amr = (AbilityManagerRemote) ic
						.lookup("swim/AbilityManager/remote");
			} catch (NamingException e) {
				e.printStackTrace();
			}
		}

		return amr;
	}

	protected static CollaborationManagerRemote getCmr() {
		if (cmr == null) {
			Context ic;
			try {
				ic = new InitialContext();
				cmr = (CollaborationManagerRemote) ic
						.lookup("swim/CollaborationManager/remote");
			} catch (NamingException e) {
				e.printStackTrace();
			}
		}

		return cmr;
	}

	protected final void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		Action calledAction = null;
		try {
			try {
				calledAction = Action.valueOf(request.getParameter(ACTION)
						.toUpperCase());
			} catch (NullPointerException e) {
				SwimLog.getSwimLog().debug(SwimServlet.class,
						e.getClass().getSimpleName() + ": don't worry!");
				calledAction = Action.DEFAULT;
			}

			if (request.getAttribute(ErrorHandling.SERVER_EXCEPTION) != null) {
				calledAction = Action.DEFAULT;
			}

			IUser owner = null;
			try {
				owner = getUmr().getTokenOwner(getToken(request));
				request.setAttribute("owner", owner);
				loadNotification(request);
			} catch (UserException e1) {
				e1.printStackTrace();
			} catch (TokenException e1) {
				e1.printStackTrace();
			} catch (SwimException e1) {
				e1.printStackTrace();
			}

			switch (calledAction) {
			case SIGNOUT:
				try {
					signOut(request, response);
				} catch (SwimException e) {
					e.printStackTrace();
					catchSwimExcpetion(calledAction, e, "/", request, response);
				}
				break;
			default:
				doRequest(request, response, Method.POST, calledAction);
				break;
			}
		} catch (Exception e) {
			e.printStackTrace();
			if (calledAction != null)
				catchGenericExcpetion(calledAction, e, request, response);
			else
				catchGenericExcpetion(Action.DEFAULT, e, request, response);
		}
	}

	protected final void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		Action calledAction = null;
		try {
			try {
				calledAction = Action.valueOf(request.getParameter(ACTION)
						.toUpperCase());
			} catch (NullPointerException e) {
				SwimLog.getSwimLog().debug(SwimServlet.class,
						e.getClass().getSimpleName() + ": don't worry!");
				calledAction = Action.DEFAULT;
			}

			IUser owner = null;
			try {
				owner = getUmr().getTokenOwner(getToken(request));
				request.setAttribute("owner", owner);
				loadNotification(request);
			} catch (UserException e1) {
				// e1.printStackTrace();
			} catch (TokenException e1) {
				// e1.printStackTrace();
			} catch (SwimException e1) {
				// e1.printStackTrace();
			}

			switch (calledAction) {

			default:
				doRequest(request, response, Method.POST, calledAction);
				break;
			}
		} catch (Exception e) {
			e.printStackTrace();
			
			if (calledAction != null)
				catchGenericExcpetion(calledAction, e, request, response);
			else
				catchGenericExcpetion(Action.DEFAULT, e, request, response);
		}

	}

	private void signOut(HttpServletRequest request,
			HttpServletResponse response) throws SwimException, IOException {
		JSONObject object = new JSONObject();
		object.accumulate("token", getToken(request));
		getUmr().signOut(object);
		request.getSession().invalidate();
		response.sendRedirect(SwimConst.Url.HOME);

	}

	protected abstract void doRequest(HttpServletRequest request,
			HttpServletResponse response, Method method, Action action)
			throws ServletException, IOException;

	private void loadNotification(HttpServletRequest request)
			throws SwimException {
		if (SwimUtils.isLogged(request)) {
			List<INotification> notifications;
			JSONObject object = new JSONObject();
			object.accumulate("token", getToken(request));
			notifications = getUmr().getLastNotification(object);

			request.setAttribute("notifications", notifications);
		}
	}

	protected void reloadNotification(HttpServletRequest request)
			throws SwimException {
		loadNotification(request);
	}

	@Deprecated
	protected static void setToken(HttpServletRequest request) {
		if (FAKE_USER_LOGGED) {
			request.getSession().setAttribute(SessionConst.TOKEN, "faketoken");
			SwimLog.getSwimLog().debug(SwimServlet.class, "Setting fake token");
		}

	}

	protected void signOut(HttpServletRequest request) throws SwimException {
		JSONObject object = new JSONObject();
		object.accumulate("token", getToken(request));
		getUmr().signOut(object);

	}

	protected String getToken(HttpServletRequest request) throws SwimException {
		if (GET_FAKE_TOKEN)
			return "debugToken";

		String token = (String) request.getSession().getAttribute(
				SessionConst.TOKEN);
		if (token == null)
			throw new SwimException("Token not found!", Severity.ERROR);
		return token;

	}

	protected void catchSwimExcpetion(Action action, SwimException e,
			String dispatcher, HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		request.setAttribute(ErrorHandling.SERVER_EXCEPTION, e);
		request.setAttribute(ErrorHandling.SERVLET_ACTION, action);
		request.setAttribute(ErrorHandling.SERVER_EXCEPTION_SEVERITY,
				e.getSeverity());
		request.setAttribute(ErrorHandling.SERVLET, this.getClass()
				.getSimpleName());

		ServletContext con = getServletContext();
		if (e.getSeverity() == Severity.FATAL_ERROR) {
			SwimLog.getSwimLog().log(SwimServlet.class,
					"FATAL_ERROR! Session is invalidated!");
			request.getSession().invalidate();
		} else if (e.getSeverity() == Severity.ERROR) {
			RequestDispatcher rd = con.getRequestDispatcher("/");
			rd.forward(request, response);
		} else {
			RequestDispatcher rd = con.getRequestDispatcher(dispatcher);
			rd.forward(request, response);
		}
	}

	protected void catchGenericExcpetion(Action action, Exception e,
			HttpServletRequest request, HttpServletResponse response)
			throws IOException {
		request.getSession().invalidate();
		request.setAttribute(ErrorHandling.SERVER_EXCEPTION, e);
		request.setAttribute(ErrorHandling.SERVLET_ACTION, action);
		request.setAttribute(ErrorHandling.SERVER_EXCEPTION_SEVERITY,
				Severity.FATAL_ERROR);
		request.setAttribute(ErrorHandling.SERVLET, this.getClass()
				.getSimpleName());

		response.sendRedirect(SwimConst.Url.HOME);
	}

	protected String getServletDispatcher(Action action) {

		String dispatcher = "/" + this.getServletContext() + "?a="
				+ action.name().toLowerCase();
		return dispatcher;
	}

	public SwimServlet() {
		super();
	}

}